﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using WatiNCF.Automater.Business;

namespace WatiNCF.Automater.Business.Builders
{
    public class AutomationTreeBuilder
    {
        public AutomationItem BuildAutomationTree(object target)
        {
            return ProcessObject(target);
        }

        private static AutomationItem ProcessObject(object target)
        {
            AutomationItem item = new AutomationItem(target.GetType().Name);
            ProcessObject(item.Items, target);
            return item;
        }

        private static void ProcessObject(ICollection<AutomationItem> items, object target)
        {
            ProcessMethods(items, target);
        }

        //private void ProcessProperties(ICollection<AutomationItem> items, object target)
        //{
        //    Type type = target.GetType();
        //    foreach (PropertyInfo info in type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
        //    {
        //        if (info.PropertyType.GetInterface("IPageController") != null)
        //        {
        //            AutomationItem item = CreateItem(info.Name, info.PropertyType);
        //            ProcessObject(item.Items, info.GetValue(target, null));
        //            items.Add(item);
        //        }
        //    }
        //}

        private static void ProcessMethods(ICollection<AutomationItem> items, object target)
        {
            ProcessMethods(items, target, string.Empty);
        }

        private static void ProcessMethods(ICollection<AutomationItem> items, object target, string methodPrefix)
        {
            const string PAGE_CONTROLLER = "IPageController";
            const string WEBSITE_CONTROLLER = "IHomePageController";
            if (target != null)
            {
                Type type = target.GetType();
                foreach (MethodInfo info in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
                {
                    Type returnType = info.ReturnType;
                    if ((returnType.GetInterface(PAGE_CONTROLLER) != null ||
                        returnType.Name == WEBSITE_CONTROLLER) && IsNotExcluded(info.Name))
                    {
                        AutomationItem item = CreateItem(string.Format("{0}{1}({2})", methodPrefix, info.Name, GetParameterString(info)), new AutomationMethod(target, info, IsLoginRequired(info)));
                        if (returnType.Name != WEBSITE_CONTROLLER)
                        {
                            IEnumerable<PropertyInfo> properties = type.GetProperties(BindingFlags.Public |
                                BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(p => p.PropertyType == info.ReturnType);
                            if (properties != null)
                            {
                                foreach (var property in properties)
                                {
                                    ProcessMethods(item.Items, property.GetValue(target, null), string.Format("{0}::", property.Name));
                                }
                            }
                        }
                        items.Add(item);
                    }
                }
            }
        }

        private static bool IsLoginRequired(MethodInfo info)
        {
            return info.Name != "Login" && info.Name != "SelectLanguage";
        }

        private static bool IsNotExcluded(string name)
        {
            return !name.StartsWith("get_") && !name.StartsWith("set_")
                && name != "Do" && name != "With" && name != "Set"
                && name != "Get" && name != "SetValue" && name != "Click";
        }

        private static string GetParameterString(MethodInfo info)
        {
            int count = 0;
            StringBuilder sb = new StringBuilder();
            ParameterInfo[] parameters = info.GetParameters();
            foreach (ParameterInfo param in parameters)
            {
                sb.Append(string.Format("{0} {1}", param.ParameterType.Name, param.Name));
                if (parameters.Length > 0 && count < parameters.Length - 1)
                {
                    sb.Append(", ");
                }

                count++;
            }

            return sb.ToString();
        }

        private static AutomationItem CreateItem(string text, object tag)
        {
            AutomationItem methodItem = new AutomationItem(text);
            methodItem.Tag = tag;
            return methodItem;
        }
    }
}
